package com.xingchi.tornado.core.scanner;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.lang.NonNull;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * 类路径下扫描器
 */
@Slf4j
public class ClassPathComponentScanner {

    private static final String RESOURCE_PATTERN = "/**/*.class";
    private static final MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();

    /**
     * 扫描指定包下实现了指定接口，或者继承了指定类的 BeanDefinition
     *
     * @param basePackage 基础包路径
     * @param targetClass 指定类
     * @return 在指定包下实现了指定接口或继承某个类的 BeanDefinition集合
     * @throws IOException IOException
     */
    public static <T> Set<BeanMetadata> findComponents(String basePackage, @NonNull Class<T> targetClass) throws IOException {
        Set<BeanMetadata> beanMetadataList = new HashSet<>();

        String packageSearchPath = PathMatchingResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;

        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resolver.getResources(packageSearchPath);

        for (Resource resource : resources) {
            if (resource.isReadable()) {
                MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                ClassMetadata classMetadata = metadataReader.getClassMetadata();
                String[] interfaceNames = classMetadata.getInterfaceNames();
                String superClassName = classMetadata.getSuperClassName();
                if (targetClass.getName().equals(superClassName)) {
                    BeanMetadata beanMetadata = obtainBeanMetadata(resource, metadataReader, classMetadata);
                    beanMetadataList.add(beanMetadata);
                    continue;
                }
                for (String interfaceName : interfaceNames) {
                    if (targetClass.getName().equals(interfaceName)) {
                        BeanMetadata beanMetadata = obtainBeanMetadata(resource, metadataReader, classMetadata);
                        beanMetadataList.add(beanMetadata);
                        break;
                    }
                }
            }
        }

        return beanMetadataList;
    }

    private static BeanMetadata obtainBeanMetadata(Resource resource, MetadataReader metadataReader, ClassMetadata classMetadata) throws IOException {
        BeanMetadata beanMetadata = new BeanMetadata();
        beanMetadata.setMetadata(metadataReader.getAnnotationMetadata());
        beanMetadata.setBeanClassName(classMetadata.getClassName());
        Class<?> sourceClass = null;
        try {
            sourceClass = ClassUtils.forName(classMetadata.getClassName(), ClassPathComponentScanner.class.getClassLoader());
        } catch (ClassNotFoundException e) {
            log.warn("无法加载指定类[{}], path: {}", classMetadata.getClassName(), resource.getFile().getAbsoluteFile(), e);
        }
        beanMetadata.setTargetClass(sourceClass);
        beanMetadata.setResource(resource);
        return beanMetadata;
    }
}
